ntfy in Docker installieren und verwenden
Wenn du ntfy docker installieren möchtest, bekommst du einen richtig angenehmen Pushserver für Benachrichtigungen, Skripte, Monitoring, Backups oder Home Assistant. ntfy ist im Prinzip ein kleiner Pub/Sub-Dienst: Du sendest eine Nachricht an ein Topic und alle abonnierten Geräte bekommen sie. Klingt simpel, ist es auch. Genau deshalb mag ich das Tool.
In diesem Beitrag zeige ich dir, wie du ntfy in Docker betreibst, Benutzer anlegst, Rechte auf Topics vergibst und welche Optionen in der Konfiguration wirklich wichtig sind. Außerdem schauen wir uns Traefik, Android, iOS, Web Push und den Emailempfang an. Also einmal ntfy mit allem, aber ohne Konfigurations-Nebelmaschine.
🛠 Voraussetzungen
Bevor es losgeht, brauchst du natürlich eine funktionierende Docker-Umgebung.
Wenn du Docker noch nicht installiert hast, findest du hier eine Schritt-für-Schritt-Anleitung, wie du das auf Ubuntu mit einem Script erledigst:
Sobald Docker läuft, brauchst du noch eine Domain oder Subdomain, wenn ntfy später sauber per HTTPS, App und Web Push erreichbar sein soll. Für lokale Tests reicht natürlich auch erstmal die Server-IP.
📦 ntfy in Docker vorbereiten
Ich lege Docker-Projekte gerne sauber in einem eigenen Ordner ab. Bei ntfy ist das besonders angenehm, weil du Konfiguration, Cache, Benutzer-Datenbank, Anhänge und Logs getrennt halten kannst.
mkdir -p /home/docker-projekte/ntfy/data/ntfy/cache mkdir -p /home/docker-projekte/ntfy/data/ntfy/etc/ntfy mkdir -p /home/docker-projekte/ntfy/data/ntfy/lib/ntfy mkdir -p /home/docker-projekte/ntfy/data/ntfy/log cd /home/docker-projekte/ntfy
Die Ordnerstruktur ist kein Muss, aber sie macht spätere Backups und Fehlersuche deutlich entspannter. Und entspannter ist bei Servern immer gut.
Die server.yml
ntfy kann über Umgebungsvariablen, Startparameter oder eine Datei konfiguriert werden. Ich bevorzuge die server.yml, weil sie lesbar bleibt und du später nicht in einem Compose-File mit 40 Environment-Zeilen suchen musst.
base-url: "https://ntfy.example.de" listen-http: ":80" cache-file: "/var/cache/ntfy/cache.db" cache-duration: "240h" auth-file: "/var/lib/ntfy/user.db" auth-default-access: "deny-all" behind-proxy: true proxy-forwarded-header: "X-Forwarded-For" enable-login: true enable-signup: false enable-reservations: true attachment-cache-dir: "/var/cache/ntfy/attachments" attachment-total-size-limit: "5G" attachment-file-size-limit: "15M" attachment-expiry-duration: "72h" upstream-base-url: "https://ntfy.sh" web-push-file: "/var/cache/ntfy/webpush.db" web-push-email-address: "admin@example.de" message-size-limit: "4k" message-delay-limit: "3d" log-level: "info" log-file: "/var/log/ntfy.log" log-format: "json"
Wichtig sind hier vor allem base-url, auth-file, auth-default-access und behind-proxy. Wenn ntfy hinter Traefik, nginx oder einem anderen Reverse Proxy läuft, muss behind-proxy: true gesetzt sein. Sonst behandelt ntfy im schlimmsten Fall alle Besucher wie eine einzige IP. Rate-Limits und Logs werden dann eher kreativ als hilfreich.
🐳 Docker Compose für ntfy
Das folgende Compose-File startet ntfy mit eigener Container-IP. Das Netzwerk ist bewusst fest definiert, damit der Container nicht irgendwo im Docker-Nirwana landet.
---
services:
ntfy:
image: binwiederhier/ntfy:v2.22.0
container_name: ntfy
hostname: ntfy
command:
- serve
environment:
TZ: Europe/Berlin
volumes:
- ./data/ntfy/cache:/var/cache/ntfy
- ./data/ntfy/etc/ntfy:/etc/ntfy
- ./data/ntfy/lib/ntfy:/var/lib/ntfy
- ./data/ntfy/log:/var/log
ports:
- "8085:80"
networks:
ntfy_net:
ipv4_address: 172.28.91.10
healthcheck:
test: ["CMD-SHELL", "wget -q --tries=1 http://localhost:80/v1/health -O - | grep -Eo '\"healthy\"\\s*:\\s*true' || exit 1"]
interval: 60s
timeout: 10s
retries: 3
start_period: 40s
restart: unless-stopped
networks:
ntfy_net:
name: ntfy.dockernetwork.local
driver: bridge
ipam:
config:
- subnet: 172.28.91.0/24
gateway: 172.28.91.1
ip_range: 172.28.91.128/25
Danach startest du den Container ganz normal:
docker compose up -d docker compose ps docker compose logs -f ntfy
Wenn du lokal testest, erreichst du ntfy anschließend über http://server-ip:8085. Produktiv würde ich ntfy aber immer über HTTPS betreiben, besonders sobald Benutzer und Passwörter ins Spiel kommen.

🚦 Erste Nachricht senden
Ein Topic musst du bei ntfy nicht vorher anlegen. Du sendest einfach eine Nachricht an den Topic-Namen. Wichtig: Mit der oben gezeigten privaten Konfiguration und auth-default-access: "deny-all" bekommst du ohne Anmeldung erstmal einen 403 forbidden. Das ist kein Fehler, sondern genau der Sinn der Sache.
Nachdem du im nächsten Abschnitt Benutzer und Rechte angelegt hast, kannst du so eine erste Nachricht senden:
curl -u patrick:DEIN_PASSWORT \ -d "Backup ist fertig" \ https://ntfy.example.de/backups

Mit Titel, Priorität und Tags wird es direkt nützlicher:
curl \ -u patrick:DEIN_PASSWORT \ -H "Title: Server Backup" \ -H "Priority: high" \ -H "Tags: floppy_disk,white_check_mark" \ -d "Das Backup von srv01 wurde erfolgreich abgeschlossen." \ https://ntfy.example.de/backups

Die ntfy-App für Android oder iOS abonnierst du dann einfach auf deine URL und das Topic, also zum Beispiel https://ntfy.example.de/backups. Auf dem Desktop geht das auch über die Weboberfläche.
🔐 Benutzer und Rechte in ntfy
Standardmäßig kann ntfy sehr offen betrieben werden. Für einen privaten Server würde ich aber fast immer mit Authentifizierung arbeiten. Der wichtigste Schalter ist:
auth-file: "/var/lib/ntfy/user.db" auth-default-access: "deny-all"
Damit ist erstmal alles zu. Danach gibst du gezielt frei, wer welches Topic lesen oder beschreiben darf.
Benutzer per Docker-Befehl anlegen
Die Benutzerverwaltung läuft direkt im Container. Für den Anfang erstellst du dir einen Admin und einen normalen Benutzer:
docker exec -it ntfy ntfy user add --role=admin admin docker exec -it ntfy ntfy user add patrick docker exec -it ntfy ntfy user add backupbot docker exec -it ntfy ntfy user list
Ein Admin darf alles lesen und schreiben. Normale Benutzer bekommen ihre Rechte über ACLs. Das ist sauberer, weil ein Backup-Script nicht direkt Herrscher über alle Topics sein muss.
Wenn du Benutzer in einem Script anlegen möchtest, geht das auch ohne interaktive Passwortabfrage:
docker exec -e NTFY_PASSWORD='SehrGeheimesPasswort' ntfy ntfy user add backupbot
Das ist besonders nützlich, wenn du ntfy automatisiert ausrollst.
Rechte auf Topics vergeben
Die Berechtigungen heißen bei ntfy recht verständlich:
rwoderread-write: lesen und schreibenrooderread-only: nur lesenwooderwrite-only: nur schreibendenyodernone: kein Zugriff
Ein typisches Setup sieht dann so aus:
docker exec -it ntfy ntfy access backupbot backups wo docker exec -it ntfy ntfy access patrick backups rw docker exec -it ntfy ntfy access everyone statusmeldungen ro docker exec -it ntfy ntfy access everyone "up*" write-only docker exec -it ntfy ntfy access
Das Beispiel macht Folgendes: Der Benutzer backupbot darf nur in das Topic backups schreiben. Der normale Benutzer patrick darf dort lesen und schreiben. Anonyme Besucher dürfen statusmeldungen lesen. Und up* ist für UnifiedPush wichtig, weil solche Topics anonym beschreibbar sein müssen, wenn du ntfy dafür nutzt.
Für Admin-Benutzer brauchst du keine ACLs. Wenn du einem Admin per ntfy access einzelne Topic-Rechte geben willst, weist ntfy dich auch genau darauf hin: Admins dürfen ohnehin alles.
Passwörter, Rollen und Tokens
Im Alltag brauchst du diese Befehle häufiger:
docker exec -it ntfy ntfy user change-pass backupbot docker exec -it ntfy ntfy user change-role backupbot admin docker exec -it ntfy ntfy user change-role backupbot user docker exec -it ntfy ntfy user del backupbot docker exec -it ntfy ntfy token add --label="backup-script" backupbot docker exec -it ntfy ntfy token list backupbot docker exec -it ntfy ntfy token remove backupbot tk_1234567890abcdef1234567890ab
Tokens sind praktisch für Skripte, weil du nicht überall dein Benutzerpasswort hinterlegen musst. Wichtig: Ein Token hat aktuell die Rechte des jeweiligen Benutzers. Wenn du also einen Token für ein Backup-Script brauchst, gib dem Benutzer nur die Topics, die er wirklich benötigt.
curl \ -H "Authorization: Bearer tk_1234567890abcdef1234567890ab" \ -d "Backup ist fertig" \ https://ntfy.example.de/backups
Das ist für Automationen meist die angenehmere Variante.
Benutzer und ACLs direkt in der Konfiguration
Wenn du deine Benutzer und Rechte lieber deklarativ pflegen möchtest, kannst du sie in der server.yml hinterlegen. Das ist für reproduzierbare Setups schön, bei Passwörtern aber etwas sensibler.
docker exec -it ntfy ntfy user hash docker exec -it ntfy ntfy token generate
Den Hash und den Token kannst du dann so in der Konfiguration verwenden:
auth-users: - "patrick:$2a$10$BEISPIELHASHBITTEERSETZEN:admin" - "backupbot:$2a$10$BEISPIELHASHBITTEERSETZEN:user" auth-access: - "backupbot:backups:wo" - "patrick:backups:rw" - "*:statusmeldungen:ro" - "*:up*:write-only" auth-tokens: - "backupbot:tk_1234567890abcdef1234567890ab:backup-script"
Bei Compose-Umgebungsvariablen musst du bei bcrypt-Hashes übrigens aufpassen, weil Dollarzeichen dort eine Sonderrolle spielen. In YAML-Dateien wie der server.yml ist das meist weniger nervig. Auch das ist ein Grund, warum ich diese Datei bevorzuge.

📲 Android, iOS und Web Push
Auf Android und iOS gibt es bei selbst gehostetem ntfy ein paar Details, die man kennen sollte. Sonst fragt man sich später, warum die Nachricht zwar irgendwann kommt, aber nicht immer sofort. Klassiker.
Android: Push ist nicht automatisch Firebase
Bei Android muss man sauber unterscheiden: Eine Benachrichtigung auf dem Handy ist erstmal einfach eine Android-Benachrichtigung. Das heißt noch nicht automatisch, dass sie über Firebase Cloud Messaging gekommen ist.
Die ntfy-App aus dem Google Play Store kann Firebase nutzen, macht das laut ntfy-Dokumentation aber nur für den öffentlichen Server ntfy.sh. Wenn du in der Play-Version deinen eigenen ntfy-Server einträgst, nutzt die App für diesen Server normalerweise Instant Delivery. Dabei hält die App selbst eine Verbindung zu deinem Server offen und zeigt dir trotzdem ganz normale Push-Benachrichtigungen an.
Dass du nicht dauerhaft eine ntfy-Benachrichtigungszeile siehst, muss dem nicht widersprechen. Je nach Android-Version, Benachrichtigungskanal und App-Einstellung kann diese Service-Benachrichtigung ausgeblendet oder anders dargestellt sein. Entscheidend ist: Für einen selbst gehosteten ntfy-Server brauchst du mit der normalen Play-App keine eigene Firebase-Konfiguration auf dem Server, damit Benachrichtigungen auf Android ankommen.
Wenn du für deinen eigenen ntfy-Server wirklich Firebase Cloud Messaging nutzen willst, brauchst du einen Firebase-Key und musst eine eigene Android-App bauen. Für die meisten privaten Setups lohnt sich das nicht. Ich würde es nur machen, wenn du genau weißt, warum du es brauchst.
firebase-key-file: "/etc/ntfy/firebase-key.json"
Die Datei würdest du dann zusätzlich in den Container mounten. Ohne eigene App bringt dir der Eintrag aber keinen echten Vorteil.
iOS Push über upstream-base-url
iOS ist strenger, wenn es um Hintergrundaktivität geht. Für schnelle Pushmeldungen auf iPhones setzt du deshalb diesen Wert:
upstream-base-url: "https://ntfy.sh"
Dabei wird nicht deine eigentliche Nachricht an ntfy.sh weitergegeben, sondern ein Poll-Hinweis. Das iPhone wird geweckt und holt sich die eigentliche Nachricht anschließend von deinem eigenen Server. Das ist ein sinnvoller Kompromiss zwischen Selbsthosting und den Apple-Regeln.
Web Push aktivieren
Für Pushmeldungen im Browser brauchst du Web-Push-Schlüssel. Die kannst du direkt im Container erzeugen:
docker exec -it ntfy ntfy webpush keys
Danach trägst du die Werte in der server.yml ein:
web-push-public-key: "DEIN_PUBLIC_KEY" web-push-private-key: "DEIN_PRIVATE_KEY" web-push-file: "/var/cache/ntfy/webpush.db" web-push-email-address: "admin@example.de"
Anschließend den Container neu starten und im Browser die Benachrichtigungen erlauben.
🌍 ntfy hinter Traefik betreiben
Wenn Traefik bereits auf deinem Docker-Host läuft, kannst du ntfy direkt per Labels veröffentlichen. Wichtig ist, dass Traefik den ntfy-Container im Docker-Netzwerk erreichen kann.
---
services:
ntfy:
image: binwiederhier/ntfy:v2.22.0
...
networks:
ntfy_net:
ipv4_address: 172.28.91.10
labels:
traefik.enable: "true"
traefik.docker.network: "ntfy.dockernetwork.local"
traefik.http.routers.ntfy.rule: "Host(`ntfy.example.de`)"
traefik.http.routers.ntfy.entrypoints: "websecure"
traefik.http.routers.ntfy.tls: "true"
traefik.http.routers.ntfy.tls.certresolver: "cloudflare"
traefik.http.services.ntfy.loadbalancer.server.port: "80"
traefik.http.services.ntfy.loadbalancer.responseforwarding.flushinterval: "-1"
traefik.http.middlewares.ntfy-headers.headers.customrequestheaders.Accept-Encoding: ""
traefik.http.middlewares.ntfy-headers.headers.customresponseheaders.X-Frame-Options: "SAMEORIGIN"
traefik.http.routers.ntfy.middlewares: "ntfy-headers"
restart: unless-stopped
networks:
ntfy_net:
name: ntfy.dockernetwork.local
driver: bridge
ipam:
config:
- subnet: 172.28.91.0/24
gateway: 172.28.91.1
ip_range: 172.28.91.128/25
Wenn dein Traefik in einem anderen Stack läuft, verbindest du ihn einmalig mit dem ntfy-Netzwerk:
docker network connect ntfy.dockernetwork.local traefik
Der Flush-Interval-Wert ist bei ntfy interessant, weil ntfy mit Streaming und offenen Verbindungen arbeitet. Außerdem solltest du in deiner server.yml unbedingt behind-proxy: true setzen.
📧 Emailempfang mit ntfy
ntfy kann nicht nur HTTP-Nachrichten empfangen, sondern auch E-Mails in Topics umwandeln. Das ist praktisch für Systeme, die nur SMTP sprechen oder bei denen Webhooks mal wieder „Enterprise“ heißen.
smtp-server-listen: ":25" smtp-server-domain: "ntfy.example.de" smtp-server-addr-prefix: "ntfy_"
Im Compose-File muss Port 25 dann veröffentlicht werden. Traefik macht dir HTTP und HTTPS, aber normales SMTP auf Port 25 ist ein anderes Spiel.
ports: - "25:25"
Im DNS brauchst du mindestens diese Einträge:
- A-Record:
mx1.ntfy.example.dezeigt auf die öffentliche IPv4 deines Servers - AAAA-Record: optional, falls du IPv6 nutzt
- MX-Record:
ntfy.example.dezeigt mit Priorität 10 aufmx1.ntfy.example.de.
Wenn du smtp-server-addr-prefix: "ntfy_" nutzt, landet eine Mail an ntfy_backups@ntfy.example.de im Topic backups. Ohne Prefix wäre es einfach backups@ntfy.example.de. Ich würde den Prefix setzen, weil du sonst sehr schnell ein hübsches kleines Spam-Ziel betreibst.
dig MX ntfy.example.de +short dig A mx1.ntfy.example.de +short
Wenn Topics geschützt sind, brauchst du auch beim E-Mail-Publishing passende Rechte. Entweder erlaubst du anonymes Schreiben für ein bestimmtes Topic oder Prefix, oder du arbeitest mit Tokens. Dann wird der Token in der Zieladresse ergänzt, zum Beispiel backups+tk_1234567890abcdef1234567890ab@ntfy.example.de.
🧰 Docker-Befehle für den Alltag
Diese Befehle brauchst du im Alltag immer wieder. Nicht spektakulär, aber genau das Zeug, das man sonst nach drei Monaten wieder googelt.
docker compose pull docker compose up -d docker compose down docker compose restart ntfy docker compose logs -f ntfy docker inspect ntfy docker exec -it ntfy sh docker exec -it ntfy ntfy user list docker exec -it ntfy ntfy access docker exec -it ntfy ntfy token list
Für einen schnellen Healthcheck kannst du auch direkt den ntfy-Endpunkt abfragen:
curl https://ntfy.example.de/v1/health
Wenn du JSON mit "healthy":true bekommst, sieht es gut aus.
🧩 Wichtige ntfy-Konfigurationsoptionen
Hier noch eine kleine Übersicht der Optionen, die ich bei ntfy am wichtigsten finde:
base-url: öffentliche URL deines ntfy-Serverscache-file: SQLite-Datei für zwischengespeicherte Nachrichtencache-duration: wie lange Nachrichten für später abrufbare Clients gespeichert bleibenauth-file: Datenbank für Benutzer und ACLsauth-default-access: Standardzugriff, zum Beispieldeny-allfür privatauth-users,auth-access,auth-tokens: deklarative Benutzer, Rechte und Tokensbehind-proxy: Pflicht hinter Reverse Proxyattachment-cache-dir: Speicherort für Anhängeupstream-base-url: schnelle iOS-Benachrichtigungen über Poll Requestsweb-push-*: Browser-Pushsmtp-server-*: eingehende E-Mails in Topicssmtp-sender-*: ausgehende E-Mail-Benachrichtigungen perX-Emailvisitor-*: Limits für Besucher, Nachrichten, E-Mails und Subscriptions
Die offizielle Dokumentation ist an der Stelle wirklich gut. Wenn du später tiefer einsteigen willst, lohnt sich besonders der Bereich zur ntfy-Konfiguration und zur ntfy-App auf dem Smartphone.
✅ Fazit
ntfy ist eines dieser Tools, die klein anfangen und dann plötzlich überall auftauchen. Erst ist es nur eine Testnachricht vom Server, dann kommen Backupmeldungen, Monitoring, Home Assistant, Watchtower, Uptime Kuma und irgendwann meldet dir sogar der Toaster seine Laune. Na gut, hoffentlich nicht.
Mit Docker ist ntfy schnell installiert. Wirklich stark wird es aber erst mit sauberer Authentifizierung, sinnvollen Topic-Rechten, HTTPS über Traefik und einer durchdachten Konfiguration für Android, iOS, Web Push und E-Mail. Dann hast du einen zuverlässigen kleinen Pushdienst, den du im Alltag wirklich gebrauchen kannst.
👥 Techniverse Community
Lust auf Austausch rund um Matrix, Selfhosting und andere smarte IT-Lösungen?
In der Techniverse Community triffst du Gleichgesinnte, kannst Fragen stellen oder einfach nerdigen Talk genießen.
👉 Jetzt der Gruppe auf Matrix beitreten
~ Direkte Raumadresse: #community:techniverse.net
👉 Für lockere Gespräche abseits der Kernthemen komm in den Talkraum
~ Direkte Raumadresse: #talk:techniverse.net
Wir freuen uns, wenn du dabei bist!



